﻿using Microsoft.Extensions.Logging;
using Sgr.Utilities;
using System.Collections.Concurrent;
using System.Text.Json;

namespace Sgr.Generator
{
    /// <summary>
    /// 基于内存和文件的号段ID生成器（无数据库依赖）
    /// </summary>
    public class DefaultSegmentNumIdGenerator : ISegmentNumIdGenerator
    {
        #region 常量定义

        private const string DEFAULT_KEY = "SGR";
        private const int DEFAULT_SEGMENT_SIZE = 1000;
        private const string DEFAULT_STORAGE_FILE = "segment-ids.json";
        private const string BACKUP_FILE_SUFFIX = ".backup";
        private const int MAX_SAVE_RETRY_COUNT = 3;

        #endregion 常量定义

        #region 私有字段

        private readonly ConcurrentDictionary<string, SegmentBuffer> _segments;
        private readonly string _storageFilePath;
        private readonly string _backupFilePath;
        private readonly int _segmentSize;
        private readonly ILogger<DefaultSegmentNumIdGenerator>? _logger;
        private readonly SemaphoreSlim _fileLock;

        private volatile bool _isInitialized;
        private volatile bool _disposed;
        private readonly object _disposeLock = new object();

        private int _pendingSaveFlag = 0; // 0=未保存, 1=保存中

        #endregion 私有字段

        #region 构造函数

        public DefaultSegmentNumIdGenerator(
            string? storageFilePath = null,
            int segmentSize = DEFAULT_SEGMENT_SIZE,
            ILogger<DefaultSegmentNumIdGenerator>? logger = null)
        {
            _segments = new ConcurrentDictionary<string, SegmentBuffer>();
            _storageFilePath = storageFilePath ?? Path.Combine(AppContext.BaseDirectory, DEFAULT_STORAGE_FILE);
            _backupFilePath = _storageFilePath + BACKUP_FILE_SUFFIX;
            _segmentSize = Math.Max(segmentSize, 100);
            _logger = logger;
            _fileLock = new SemaphoreSlim(1, 1);
            _isInitialized = false;
            _disposed = false;
        }

        #endregion 构造函数

        #region ISegmentNumIdGenerator 实现

        public async Task<bool> InitAsync(CancellationToken cancellationToken = default)
        {
            if (_isInitialized)
                return true;

            ThrowIfDisposed();

            try
            {
                _logger?.LogInformation("开始初始化号段ID生成器，存储文件: {FilePath}", _storageFilePath);

                await LoadSegmentsFromFileAsync(cancellationToken);

                _isInitialized = true;
                _logger?.LogInformation("号段ID生成器初始化完成");

                return true;
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "号段ID生成器初始化失败");
                return false;
            }
        }

        public async Task<long> GetUniqueIdAsync(string key = "", CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            if (!_isInitialized)
            {
                var initSuccess = await InitAsync(cancellationToken);
                if (!initSuccess)
                {
                    throw new InvalidOperationException("号段ID生成器初始化失败");
                }
            }

            if (string.IsNullOrEmpty(key))
            {
                key = DEFAULT_KEY;
            }

            var buffer = _segments.GetOrAdd(key, k => new SegmentBuffer(k, _segmentSize));
            return await GetIdFromBufferAsync(buffer, cancellationToken);
        }

        public void Dispose()
        {
            if (_disposed)
                return;

            lock (_disposeLock)
            {
                if (_disposed)
                    return;

                try
                {
                    // 优化：同步保存，但使用超时机制避免长时间阻塞
                    if (_isInitialized)
                    {
                        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
                        try
                        {
                            SaveSegmentsToFileAsync(cts.Token).GetAwaiter().GetResult();
                        }
                        catch (OperationCanceledException)
                        {
                            _logger?.LogWarning("保存号段信息超时，可能导致部分数据丢失");
                        }
                    }

                    // 释放资源
                    _fileLock?.Dispose();
                    _logger?.LogInformation("号段ID生成器已释放资源");
                }
                catch (Exception ex)
                {
                    _logger?.LogError(ex, "释放号段ID生成器资源时发生错误");
                }
                finally
                {
                    _disposed = true;
                }
            }

            GC.SuppressFinalize(this);
        }

        #endregion ISegmentNumIdGenerator 实现

        #region 核心逻辑

        private async Task<long> GetIdFromBufferAsync(SegmentBuffer buffer, CancellationToken cancellationToken)
        {
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (buffer.TryGetNextId(out long id))
                {
                    return id;
                }

                await RefillBufferAsync(buffer, cancellationToken);
            }
        }

        private async Task RefillBufferAsync(SegmentBuffer buffer, CancellationToken cancellationToken)
        {
            await _fileLock.WaitAsync(cancellationToken);
            try
            {
                // 双重检查
                if (buffer.TryGetNextId(out _))
                {
                    return;
                }

                _logger?.LogDebug("为业务键 {Key} 分配新号段", buffer.Key);

                long newStartId = buffer.MaxId + 1;
                long newMaxId = newStartId + _segmentSize - 1;

                buffer.RefillSegment(newStartId, newMaxId);

                // 批量保存，减少文件IO
                QueueSave();

                _logger?.LogDebug("业务键 {Key} 新号段分配完成: {StartId}-{MaxId}",
                    buffer.Key, newStartId, newMaxId);
            }
            finally
            {
                _fileLock.Release();
            }
        }

        /// <summary>
        /// 优化：队列化保存，避免频繁文件操作
        /// </summary>
        private void QueueSave()
        {
            // 原子操作：只有当 _pendingSave 为 0 时才设置为 1
            if (Interlocked.CompareExchange(ref _pendingSaveFlag, 1, 0) == 1)
            {
                return; // 已有保存操作在进行或排队中
            }

            // 启动后台保存任务
            _ = Task.Run(async () =>
            {
                try
                {
                    // 延迟合并多个保存请求
                    await Task.Delay(100);

                    // 执行实际保存
                    await SaveSegmentsToFileAsync();

                    _logger?.LogDebug("批量保存号段信息完成");
                }
                catch (Exception ex)
                {
                    _logger?.LogError(ex, "保存号段信息到文件失败");
                }
                finally
                {
                    // 重置标志，允许下次保存
                    Interlocked.Exchange(ref _pendingSaveFlag, 0);
                }
            });
        }

        #endregion 核心逻辑

        #region 文件持久化

        /// <summary>
        /// 优化：增强的文件加载，支持备份恢复
        /// </summary>
        private async Task LoadSegmentsFromFileAsync(CancellationToken cancellationToken)
        {
            // 尝试从主文件加载
            if (await TryLoadFromFileAsync(_storageFilePath, cancellationToken))
            {
                return;
            }

            // 主文件加载失败，尝试从备份文件加载
            if (File.Exists(_backupFilePath))
            {
                _logger?.LogWarning("主文件加载失败，尝试从备份文件恢复: {BackupPath}", _backupFilePath);

                if (await TryLoadFromFileAsync(_backupFilePath, cancellationToken))
                {
                    _logger?.LogInformation("从备份文件成功恢复号段信息");
                    return;
                }
            }

            _logger?.LogInformation("无可用的号段存储文件，将从头开始生成ID");
        }

        private async Task<bool> TryLoadFromFileAsync(string filePath, CancellationToken cancellationToken)
        {
            if (!File.Exists(filePath))
            {
                return false;
            }

            try
            {
                var json = await File.ReadAllTextAsync(filePath, cancellationToken);
                if (string.IsNullOrWhiteSpace(json))
                {
                    return false;
                }

                //var data = JsonSerializer.Deserialize<Dictionary<string, SegmentData>>(json);
                var data = JsonHelper.DeserializeObject<Dictionary<string, SegmentData>>(json);
                if (data == null || data.Count == 0)
                {
                    return false;
                }

                // 验证数据完整性
                foreach (var kvp in data)
                {
                    if (string.IsNullOrEmpty(kvp.Key) || kvp.Value == null || kvp.Value.MaxId < 0)
                    {
                        _logger?.LogWarning("检测到无效的号段数据: {Key}", kvp.Key);
                        continue;
                    }

                    var buffer = new SegmentBuffer(kvp.Key, _segmentSize);
                    buffer.LoadFromData(kvp.Value);
                    _segments[kvp.Key] = buffer;
                }

                _logger?.LogInformation("从文件 {FilePath} 加载了 {Count} 个业务键的号段信息",
                    filePath, _segments.Count);
                return true;
            }
            catch (JsonException ex)
            {
                _logger?.LogError(ex, "号段文件格式错误: {FilePath}", filePath);
                return false;
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "从文件加载号段信息失败: {FilePath}", filePath);
                return false;
            }
        }

        /// <summary>
        /// 优化：增强的文件保存，支持备份和重试
        /// </summary>
        private async Task SaveSegmentsToFileAsync(CancellationToken cancellationToken = default)
        {
            if (_segments.IsEmpty)
                return;

            var data = new Dictionary<string, SegmentData>();
            foreach (var kvp in _segments)
            {
                data[kvp.Key] = kvp.Value.ToData();
            }

            //var json = JsonSerializer.Serialize(data, new JsonSerializerOptions
            //{
            //    WriteIndented = true
            //});

            var json = JsonHelper.SerializeObject(data);

            // 重试机制
            for (int attempt = 1; attempt <= MAX_SAVE_RETRY_COUNT; attempt++)
            {
                try
                {
                    // 确保目录存在
                    var directory = Path.GetDirectoryName(_storageFilePath);
                    if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
                    {
                        Directory.CreateDirectory(directory);
                    }

                    // 先创建备份
                    if (File.Exists(_storageFilePath))
                    {
                        File.Copy(_storageFilePath, _backupFilePath, overwrite: true);
                    }

                    // 写入主文件
                    await File.WriteAllTextAsync(_storageFilePath, json, cancellationToken);

                    _logger?.LogDebug("号段信息已保存到文件: {FilePath}", _storageFilePath);
                    return;
                }
                catch (Exception ex) when (attempt < MAX_SAVE_RETRY_COUNT)
                {
                    _logger?.LogWarning(ex, "保存号段信息失败 (尝试 {Attempt}/{MaxAttempts}): {FilePath}",
                        attempt, MAX_SAVE_RETRY_COUNT, _storageFilePath);

                    await Task.Delay(100 * attempt, cancellationToken); // 递增延迟
                }
                catch (Exception ex)
                {
                    _logger?.LogError(ex, "保存号段信息到文件失败，已达最大重试次数: {FilePath}", _storageFilePath);
                    throw;
                }
            }
        }

        #endregion 文件持久化

        #region 辅助方法

        private void ThrowIfDisposed()
        {
            ObjectDisposedException.ThrowIf(_disposed, this);
        }

        #endregion 辅助方法

        #region 内部类型

        private class SegmentBuffer
        {
            private readonly object _lock = new object();
            private long _currentId;
            private long _maxId;

            public string Key { get; }
            public int SegmentSize { get; }
            public long MaxId => _maxId;

            public SegmentBuffer(string key, int segmentSize)
            {
                Key = key;
                SegmentSize = segmentSize;
                _currentId = 0;
                _maxId = 0;
            }

            public bool TryGetNextId(out long id)
            {
                lock (_lock)
                {
                    if (_currentId < _maxId)
                    {
                        id = ++_currentId;
                        return true;
                    }

                    id = 0;
                    return false;
                }
            }

            public void RefillSegment(long startId, long maxId)
            {
                lock (_lock)
                {
                    _currentId = startId - 1;
                    _maxId = maxId;
                }
            }

            public void LoadFromData(SegmentData data)
            {
                lock (_lock)
                {
                    _currentId = data.CurrentId;
                    _maxId = data.MaxId;
                }
            }

            public SegmentData ToData()
            {
                lock (_lock)
                {
                    return new SegmentData
                    {
                        CurrentId = _currentId,
                        MaxId = _maxId
                    };
                }
            }
        }

        private class SegmentData
        {
            public long CurrentId { get; set; }
            public long MaxId { get; set; }
        }

        #endregion 内部类型
    }
}